#!/bin/python3
import numpy as np
import matplotlib.pyplot as plt
import sys
import getopt
import datetime
import os
import time
import pickle
import re
from pathlib import Path
from matplotlib.pyplot import MultipleLocator

#曲线图类，包含图形的绘制和控制
class curves:   #曲线图
    picture_size=[20, 5]
    figure = plt.figure(figsize=picture_size)  # 当前绘图使用的平台

    line_range=[0,-1]    #控制x轴的显示范围

    x_axis=[]
    # @x:x轴是固定的
    # @y:为一个二维数组，记录各条曲线的数据
    # @names:每条曲线的名称，和ys一一对应
    def __init__(self,x,ys,names,title=''):
        self.x_axis=x
        self.create_picture(x,ys,names,title)
    
    def save(self,outfile):
        plt.savefig(outfile)
        print('图片保存至\''+outfile+'\'')
    
    def show(self):
        plt.legend()
        plt.show()
    
    def create_picture(self,x_axis, y_axis, name, title):  #创建图片
        plt.title(title)
        for i in range(len(name)):
            plt.plot(x_axis[self.line_range[0]:self.line_range[1]],y_axis[i][self.line_range[0]:self.line_range[1]],label=name[i])
    
    def add(self,x=[],y=[],label='no-curve-label'):
        if []==x :
            if []==self.x_axis:
                return label
            else:
                x=self.x_axis
        plt.plot(x,y,label=label)
    
    def update(self,x,y,label,title):
        plt.title(title)
        plt.cla()
        plt.plot(x,y,label=label)

class fun_struct: 
    def __init__(self):
        self.name=''
        self.id=0
    
class ffung:    #对ffun数据进行处理
    data=[]
    data_len=0
    datafile=''

    cpu_count=0
    cpu_list=[]

    fun_count=0 #函数数量
    fun_list=object

    varfile='ffun.var'

    time_span=1
    timesec=[]
    timesec_len=0

    stat_array=[]

    allcpu_stat_array=[]
    def __init__(self,inputfile):
        self.datafile=inputfile
    
    def init_value(self):
        self.timesec=[self.cut_dec(i, 6) for i in np.linspace(self.cut_dec(self.data[1][0], 2), self.cut_dec(self.data[1][-1], 2)+2*self.time_span,int((self.cut_dec(self.data[1][-1], 2)-self.cut_dec(self.data[1][0], 2)+2*self.time_span)/self.time_span)+1)]
        self.timesec_len=len(self.timesec)
        #统计数组
        self.statistical()
        #将所有cpu上的统计结果相加，获得整个进程的结果
        self.allcpu_stat_array=sum([self.stat_array[i] for i in self.cpu_list])
    
    def cut_dec(self,db,num):
        tmp=str(db).split('.')
        return float(tmp[0]+'.'+tmp[1][:num])


    def open(self,inputfile=''):    #打开目标文件并读取数据
        if ''!=inputfile:
            self.datafile=inputfile
        if not self.isfile:
            return False
        try:
            with open(self.datafile,'rb') as fin:
                #读取cpu
                line=str(fin.readline(),'ascii')
                tmp=re.findall(r'cpus=(\d*)', line)
                if []!=tmp:
                    self.cpu_count=int(tmp[0])
                    self.cpu_list=[i for i in range(self.cpu_count)]
                else:
                    return False
                #读取函数
                line=str(fin.readline(),'ascii')
                self.fun_count=int(re.findall(r'funs=(\d*)',line)[0])
                if 0>=self.fun_count:
                    return False
                self.fun_list=[fun_struct() for i in range(self.fun_count)]
                for i in range(self.fun_count):
                    line_sp=str(fin.readline(),'ascii').split()
                    self.fun_list[i].name=line_sp[0]
                    self.fun_list[i].id=line_sp[1]
                #读取数据
                errnum=0
                last_time=float(0)
                data_tmp=[]
                for i in fin:
                    dataline=i.split()
                    new_time=float(dataline[1])
                    try:
                        data_tmp.append([int(dataline[0]),float(new_time+last_time),int(dataline[2])])
                    except Exception as err:
                        errnum+=1
                        continue
                    last_time+=new_time
                self.data=list(zip(*(data_tmp)))
                self.data_len=len(self.data[1])
                print('读取文件出错次数:%d' % errnum)
                #保存数据
                #self.save_var(self.data,self.varfile)
        except OSError as file_open_error:
            print('文件 %s 打开失败：' % self.datafile)
            print(file_open_error)
            return False
        return True
                
    def save_var(self,data,varfile):    #保存数据
        try:
            with open(varfile, 'wb') as varfout:
                for i in data:
                    pickle.dump(i, varfout)
        except OSError as file_open_error:
            print(file_open_error)
            print('打开文件：\''+varfile+'\'失败')
            return False
        return True

    def statistical(self):  #统计数据
        self.stat_array=np.zeros((self.cpu_count,self.fun_count,self.timesec_len),dtype=int,order='C')
        #统计每个函数在每个cpu中出现的次数
        y, item = 1, 0
        if 100<self.data_len:
            tmp=float(self.data_len/100)
        else:
            tmp=float(1)
        errnum=0
        for i in self.data[1]:
            if i > self.timesec[y]:
                y+=1
            try:
                #print('%d,%d,%d' % (item,self.data[0][item],int(self.data[2][item])))
                self.stat_array[self.data[0][item]][int(self.data[2][item])][y]+=1
            except Exception as err:
                errnum+=1
                item+=1
                continue
            item+=1
            if 0 == item % int(tmp):
                self.process_bar(item/tmp/100, start_str='',end_str='100%', total_length=60)
        self.process_bar(1.0, start_str='',end_str='100%', total_length=60)
        print()
        print('出错次数：%d' % errnum)
            
                
    def process_bar(self,percent, start_str='', end_str='', total_length=0):
        # 进度条，percent要大于0
        bar = ''.join(["\033[31m%s\033[0m" % '#'] *
                      int(percent * total_length)) + ''
        bar = '\r' + start_str + \
            bar.ljust(int(percent*total_length)) + \
            ' {:0>4.1f}%|'.format(percent*100) + end_str
        print(bar, end='', flush=True)
    
    def isfile(self):   #判断目标文件是否存在
        datfile_path = Path(self.datafile)
        if not datfile_path.is_file():
            print('文件\''+self.datafile+'\'不存在')
            return False
        return True
    
    def bakeup(self,file):  #将目标文件重命名
        try:
            os.rename(file,file+'.bak')
        except Exception as err:
            print('重命名失败')
            print(err)
            return False
        return True

def main(argv):
    main_options='hi:o:st:'
    main_options_name=['help','ifile=','ofile=','show','timespan=']
    inputfile='ffun.data'
    outputfile='ffun.jpg'
    timespan=0.1
    show_graph=False
    try:
        opts, args = getopt.getopt(argv, main_options, main_options_name)
    except getopt.GetoptError as error:
        return
    for opt,arg in opts:
        if opt in ('-h','--help'):
            print('python3 fung.py <options> -i xxx.data')
            print('-o\t输出图片名')
            print('-s\t展示图片')
            print('-t\t设置时间轴刻度间隔，单位\'秒\'')
            return 
        elif opt in ('-i','--ifile'):
            inputfile=arg
        elif opt in ('-o','--ofile'):
            outputfile=arg
        elif opt in ('-s','--show'):
            show_graph=True
        elif opt in ('-t','--timespan'):
            timespan=float(arg)
    d=ffung(inputfile)
    if not d.open():
        return
    d.time_span=timespan
    d.init_value()
    c=curves(d.timesec,d.allcpu_stat_array,[i.name for i in d.fun_list],'ffung-test')
    c.save(outputfile)
    if show_graph:
        c.show()

if __name__=='__main__':
    main(sys.argv[1:])